home *** CD-ROM | disk | FTP | other *** search
- /*
- ** MAIL2NEWS
- ** Gateway mail messages into netnews. Usage:
- ** mail2news [inews flags] -o Organization
- ** In order to do this, there are a number of interesting transformations
- ** that need to be made on the headers...
- **
- ** This program is descended from: @(#)recnews.c 2.10 4/16/85.
- */
- #include "gate.h"
- #include <signal.h>
- #include <sys/file.h>
- #if defined(RCSID)
- static char RCS[] =
- "$Header: /nfs/papaya/u2/rsalz/src/newsgate/RCS/mail2news.c,v 1.19 91/07/18 18:41:45 rsalz Exp $";
- #endif /* defined(RCSID) */
-
-
- #define WAIT_CORED(s) (s & 0200)
- #define WAIT_EXITSIG(s) (s & 0177)
- #define WAIT_EXITCODE(s) ((s >> 8) & 0377)
-
- /* For those who don't have this in <sys/file.h>. */
- #if !defined(R_OK)
- #define R_OK 4
- #endif /* !defined(R_OK) */
-
- /* Stuff for pipe(2). */
- #define STDIN 0
- #define PIPE_READER 0
- #define PIPE_WRITER 1
-
-
- /* Global variables. */
- STATIC int Debugging;
- STATIC int ChildPid = -1;
- char *Pname;
-
-
-
- /*
- ** Go down to the absolute minimum.
- */
- STATIC void
- TrimEnvironment()
- {
- static char *Empty[] = { NULL };
-
- environ = Empty;
- }
-
-
- /*
- ** Quickie hack to see of a mail message is a "please drop me" request.
- ** Reads the message on the input file, and returns NULL if it should
- ** be ignored, or a FILE handle if we should process it.
- **
- ** The original stand-alone program written was Russ Nelson,
- ** <nelson@clutx.clarkson.edu>. I hacked on it, and made it into a
- ** subroutine. Perhaps a better way to test is to make the test less
- ** conservative, and see what "real" articles get caught, and make
- ** adjustments then? Comments solicited.
- */
- STATIC FILE *
- IsSubRequest(F)
- register FILE *F;
- {
- register FILE *Out;
- register char *p;
- register int c;
- register int drop_or_add;
- register int from_or_to;
- register int mail_word;
- register int count;
- char word[SM_SIZE];
- char buff[SM_SIZE];
-
- /* Create temp file; if we can't let the message through. */
- if ((Out = fopen(mktemp(strcpy(buff, TEMPFILE)), "w")) == NULL)
- return F;
-
- /* Clear counts. */
- drop_or_add = 0;
- from_or_to = 0;
- mail_word = 0;
- count = 0;
-
- /* Read input a word at a time. */
- for (p = word; (c = getc(F)) != EOF; ) {
- (void)putc(c, Out);
- if (!isalpha(c)) {
- *p = '\0';
- if (p > word)
- count++;
- p = word;
-
- if (EQ(word, "remove") || EQ(word, "drop") || EQ(word, "off")
- || EQ(word, "subscribe") || EQ(word, "get") || EQ(word, "add"))
- drop_or_add++;
- else if (EQ(word, "from") || EQ(word, "to"))
- from_or_to++;
- else if (EQ(word, "mail") || EQ(word, "mailing")
- || EQ(word, "list") || EQ(word, "dl"))
- mail_word++;
- }
- else if (p < &word[sizeof word - 1])
- *p++ = isupper(c) ? tolower(c) : c;
- }
-
- (void)fclose(F);
- (void)fclose(Out);
-
- /* Use fancy-shmancy AI techniques to determine what the message is. */
- c = count < 25 && drop_or_add && from_or_to && mail_word;
- F = c ? NULL : fopen(buff, "r");
-
- (void)unlink(buff);
- return F;
- }
-
-
- /*
- ** Modify the Newsgroups: as directed by the command string.
- */
- STATIC void
- DoCommand(hp, command, group)
- register HBUF *hp;
- char *command;
- char *group;
- {
- register char *p;
- register int i;
- register int n;
- register int nng;
- char **tokens;
- char **ng;
- char buff[BUFSIZ];
-
- if ((n = Split(command, &tokens, '\0')) == 0) {
- SplitFree(&tokens);
- return;
- }
-
- nng = Split(hp->nbuf, &ng, NGDELIM);
- p = hp->nbuf;
- switch (tokens[0][0]) {
- case 'a': /* Add */
- if (n > 1)
- for (p += strlen(p), i = 1; i < n; i++) {
- *p++ = NGDELIM;
- p += APPEND(p, tokens[i]);
- }
- break;
- case 'd': /* Delete */
- for (i = 0; i < nng; i++)
- if (!EQ(ng[i], group)) {
- if (p > hp->nbuf)
- *p++ = NGDELIM;
- p += APPEND(p, ng[i]);
- }
- if (p == hp->nbuf)
- Strcpy(hp->nbuf, "junk");
- break;
- case 'k': /* Kill */
- Fprintf(stderr, "%s: Your posting to %s was killed by %s.\n",
- Pname, hp->nbuf, n > 1 ? tokens[1] : group);
- exit(EX_NOPERM);
- /* NOTREACHED */
- case 'm': /* Move */
- if (n > 1)
- if (nng == 1)
- Strcpy(hp->nbuf, tokens[1]);
- else
- for (i = 0; i < nng; i++) {
- if (p > hp->nbuf)
- *p++ = NGDELIM;
- p += APPEND(p, EQ(ng[i], group) ? tokens[1] : ng[i]);
- }
- break;
- case 'q': /* Quiet kill */
- if (Debugging) {
- (void)printf("Quiet kill (ignored for debugging).\n");
- break;
- }
- /* Eat the message up, and pretend we delivered it. */
- while (fgets(buff, sizeof buff, stdin))
- continue;
- exit(EX_OK);
- /* NOTREACHED */
- }
-
- SplitFree(&tokens);
- SplitFree(&ng);
- }
-
-
- /*
- ** For Ozan Yigit's public domain regex.
- */
- /* ARGSUSED */
- void
- re_fail(text, arg)
- char *text;
- int arg;
- {
- }
-
-
- /*
- ** Split a line that looks like XpatternXcommandX into the pattern and
- ** the command. Initialize the RE matcher with the pattern, and return
- ** the command.
- */
- STATIC char *
- ParsePattern(p, lineno)
- register char *p;
- int lineno;
- {
- register char *cp;
- register char *command;
- register char delim;
- char *RE;
-
- /* Ignore comments and blank lines. */
- if (*p == '#' || *p == '\0')
- return NULL;
-
- for (delim = *p++, RE = cp = p, command = NULL; *cp; *p++ = *cp++)
- if (*cp == '\\' && cp[1] == delim)
- cp++;
- else if (*cp == delim) {
- /* Found delimiter; mark command, terminate RE. */
- command = ++cp;
- *p = '\0';
- break;
- }
-
- if (command == NULL || *command == '\0')
- Fprintf(stderr, "%s: Incomplete regular expression, line %d.\n",
- Pname, lineno);
- else if ((cp = re_comp(RE)) != NULL)
- Fprintf(stderr, "%s: Bad regular expression, line %d: %s.\n",
- Pname, lineno, cp);
- else
- return command;
-
- #if defined(lint)
- /* My, my, aren't we anal. */
- (void)re_subs("", "");
- re_modw("");
- #endif /* defined(lint) */
-
- return NULL;
- }
-
-
- /*
- ** Convert string to lower case, return a new copy.
- */
- STATIC char *
- MakeLowerCopy(s)
- register char *s;
- {
- register char *p;
-
- for (p = s = COPY(s); *p; p++)
- if (isupper(*p))
- *p = tolower(*p);
- return s;
- }
-
-
- /*
- ** Free up something that ReadFile made.
- */
- void
- FreeFile(V)
- char **V;
- {
- register char **p;
-
- if ((p = V) != NULL) {
- while (*p)
- free(*p++);
- free((char *)V);
- }
- }
-
-
- /*
- ** Change newsgroups if the Subject:, Keywords:, or Summary: match a
- ** pattern found in the newsgroup remap file.
- */
- STATIC void
- Editnewsgroups(hp)
- register HBUF *hp;
- {
- register char *p;
- register int n;
- register int i;
- register int j;
- register int t;
- char **groups;
- char **mapline;
- char *hdrline[4];
- char buff[LG_SIZE];
-
- /* Copy some headers, but if nothing's there, give up. */
- i = 0;
- if (hp->title[0])
- hdrline[i++] = MakeLowerCopy(hp->title);
- if (hp->keywords[0])
- hdrline[i++] = MakeLowerCopy(hp->keywords);
- if (hp->summary[0])
- hdrline[i++] = MakeLowerCopy(hp->summary);
- if (i == 0)
- return;
- hdrline[i] = NULL;
-
- /* For all the newsgroups, see if there's a mapping file. */
- for (n = Split(hp->nbuf, &groups, NGDELIM), i = 0; i < n; i++) {
- if (groups[i] == NULL || groups[i][0] == '\0')
- continue;
-
- /* Gate the name of the mapping file. */
- #if defined(IN_ONEPLACE)
- Strcpy(buff, IN_ONEPLACE);
- #endif /* defined(IN_ONEPLACE) */
- #if defined(IN_SPOOLDIR)
- {
- register char *q;
-
- for (p = buff + APPEND(buff, IN_SPOOLDIR), q = groups[i]; *q; q++)
- *p++ = *q == '.' ? '/' : *q;
- Strcpy(p, "/recnews.cmd");
- }
- #endif /* defined(IN_SPOOLDIR) */
- #if defined(IN_CMDDIR)
- Sprintf(buff, "%s/%s", IN_CMDDIR, groups[i]);
- #endif /* defined(IN_CMDDIR) */
-
- if (access(buff, R_OK) >= 0 && (mapline = ReadFile(buff))) {
- /* For all lines in the file, if there's a command and the
- * pattern matches, execute the command. */
- for (j = 0; mapline[j]; j++)
- if ((p = ParsePattern(mapline[j], j)) != NULL)
- for (t = 0; hdrline[t]; t++)
- if (re_exec(hdrline[t]) == 1) {
- DoCommand(hp, p, groups[i]);
- break;
- }
- FreeFile(mapline);
- }
- }
-
- /* Free dynamic space. */
- for (i = 0; hdrline[i]; i++)
- free(hdrline[i]);
- SplitFree(&groups);
- }
-
-
- /*
- ** Signal-catcher and child-reapers.
- */
-
-
- /*
- ** Exit such that sendmail will again later.
- */
- STATIC CATCHER
- Sig_tempfail()
- {
- exit(EX_TEMPFAIL);
- }
-
-
- /*
- ** Reap the inews child properly, and exit with his exit code, so that
- ** ultimate success or failure rests with inews.
- */
- STATIC CATCHER
- childgone()
- {
- register int pid;
- int W;
-
- /* Some systems get race conditions, causing this routine to be
- * invoke multiple times, sigh. Some systems want SIG_IGN, I think. */
- #if defined(SIGCHLD)
- Signal(SIGCHLD, SIG_DFL);
- #endif /* defined(SIGCHLD) */
- #if defined(SIGCLD)
- Signal(SIGCLD, SIG_DFL);
- #endif /* defined(SIGCLD) */
-
- if ((pid = wait(&W)) != ChildPid || pid == -1)
- exit(EX_OSERR);
-
- /* Was it a good death? */
- if (WAIT_EXITSIG(W)) {
- Fprintf(stderr, "%s: Child %d killed by signal %d.\n",
- Pname, pid, WAIT_EXITSIG(W));
- if (WAIT_CORED(W))
- Fprintf(stderr, "%s: Child %d dumped core.\n", Pname, pid);
- exit(EX_SOFTWARE);
- }
-
- #if defined(MMDF)
- /* We need a way to tell temporary errors from permanent ones. Inews
- * will reject messages because of too much quoting, for example,
- * so the message will sit in the queue forever. Until then we'll
- * have to lose messages on any error. */
- exit(0);
- #else
- exit(WAIT_EXITCODE(W));
- #endif /* defined(MMDF) */
- }
-
-
-
- /*
- ** Convert the characters following dots to upper case, if they're
- ** lower case. Two dots in a row will leave one dot in their place.
- ** Modifies the argument.
- */
- STATIC char *
- HackPeriods(string)
- char *string;
- {
- register char *s;
- register char *p;
-
- if (string) {
- for (p = s = string; *p; *s++ = *p++)
- if (*p == '.') {
- if (*++p == '\0') {
- *s++ = '.';
- break;
- }
- if (islower(*p))
- *p = toupper(*p);
- }
- *s = '\0';
- }
- return string;
- }
-
-
-
- main(ac, av)
- register int ac;
- register char *av[];
- {
- register char **vec;
- register char *p;
- register FILE *F;
- register FILE *Infile;
- HBUF H;
- char **iv;
- char buff[BUFSIZ];
- #if defined(SENDMAIL)
- char sendbuff[sizeof buff];
- #endif /* defined(SENDMAIL) */
- int fd[2];
- int FlushSubRequests;
- int SubjectRequired;
- int GotEquals;
-
- if ((Pname = RDX(av[0], '/')) == NULL)
- Pname = av[0];
- else
- Pname++;
- Infile = stdin;
-
- /* Remove any trace of who we are. */
- TrimEnvironment();
-
- /* So that cores will actually drop... */
- if (chdir("/tmp") < 0) {
- Fprintf(stderr, "%s: Can't chdir(/tmp), %s.\n", Pname,
- strerror(errno));
- exit(EX_TEMPFAIL);
- }
-
- /* If someone wants to shut down the system, tell sendmail to
- * try again later. */
- Signal(SIGTERM, Sig_tempfail);
-
- #if defined(SENDMAIL)
- /* First read should fetch us the UNIX From_ line. Not done in MMDF. */
- if (fgets(buff, sizeof buff, Infile) == NULL)
- exit(EX_NOINPUT);
-
- #if defined(REQUIRE_UNIX_FROM)
- if (!EQn(buff, "From ", 5)) {
- Fprintf(stderr, "%s: Input didn't start with UNIX From line:\n",
- Pname);
- Fprintf(stderr,"\t%s.\n", buff);
- exit(EX_DATAERR);
- }
- #endif /* defined(REQUIRE_UNIX_FROM) */
-
- /* Turn "From foo ... remote from bar ..." into "Sender: bar!foo". */
- if (EQn(buff, "From ", 5)) {
- Strcpy(sendbuff, "Sender: ");
- for (p = buff + 4; (p = IDX(p + 1, 'r')) != NULL; )
- if (strncmp(p, "remote from ", 12) == 0
- && sscanf(p, "remote from %s", sendbuff + 8) == 1) {
- Strcat(sendbuff, "!");
- break;
- }
- if (sscanf(buff, "From %s", sendbuff + strlen(sendbuff)) == 1)
- Strcpy(buff, sendbuff);
- }
- #endif /* defined(SENDMAIL) */
-
- /* Read the mail header. */
- rfc822read(&H, Infile, buff, sizeof buff);
-
- /* Process the argument list, copying anything that we don't recognize
- * over to the inews argument list and changing things as we see fit. */
- FlushSubRequests = FALSE;
- GotEquals = FALSE;
- SubjectRequired = TRUE;
- iv = NEW(char*, ac+ 2);
- iv[0] = INEWS;
- iv[1] = "-h";
- for (vec = iv + 2; (p = *++av) != NULL; )
- if (p[0] != '-')
- *vec++ = p;
- else
- switch(p[1]) {
- case 'x':
- SubjectRequired = FALSE;
- /* FALLTHROUGH */
- default:
- *vec++ = p;
- break;
- case '=':
- if (!GotEquals)
- iv++;
- iv[0] = p[2] ? &p[2] : *++av;
- GotEquals++;
- break;
- case '.':
- Debugging++;
- break;
- case 'n':
- /* Newsgroup this messages goes to. */
- Strcpy(H.nbuf, p[2] ? &p[2] : *++av);
- break;
- case 'o':
- /* Default organization. */
- p = p[2] ? &p[2] : *++av;
- if (H.organization[0] == '\0')
- Strcpy(H.organization, HackPeriods(p));
- SubjectRequired = FALSE;
- break;
- case 'd':
- /* Default distribution. */
- p = p[2] ? &p[2] : *++av;
- if (H.distribution[0] == '\0')
- Strcpy(H.distribution, p);
- break;
- case 'a':
- /* Default approval. */
- p = p[2] ? &p[2] : *++av;
- if (H.approved[0] == '\0')
- Strcpy(H.approved, p);
- break;
- case 'F':
- FlushSubRequests = TRUE;
- break;
- }
- *vec++ = NULL;
-
- /* Bash on the mail header. */
- if ((p = HackHeader(&H, SubjectRequired)) != NULL) {
- Fprintf(stderr, "%s: Rejected by netnews because:\n", Pname);
- Fprintf(stderr, "\t%s.\n", p);
- if (H.nbuf[0])
- Fprintf(stderr, "\tIt was going into the newsgroup%s %s.\n",
- IDX(H.nbuf, NGDELIM) ? "s" : "", H.nbuf);
- exit(EX_DATAERR);
- }
- Editnewsgroups(&H);
-
- if (Debugging) {
- for (vec = iv; *vec; vec++)
- (void)printf(" |%s| ", *vec);
- (void)printf("\n");
- if (!rfc822write(&H, stdout))
- Fprintf(stderr, "%s: Can't write header, %s.\n",
- Pname, strerror(errno));
- while (fgets(buff, sizeof buff, Infile))
- Fputs(buff, stdout);
- exit(EX_OK);
- }
-
- if (FlushSubRequests && (Infile = IsSubRequest(Infile)) == NULL) {
- Fprintf(stderr, "%s: Rejected by netnews becase:\n", Pname);
- Fprintf(stderr, "\tIt seems like a subscription request.\n");
- exit(EX_DATAERR);
- }
-
- /* Get ready to spawn an inews. */
- if (pipe(fd) < 0) {
- Fprintf(stderr, "%s: Can't pipe, %s.\n", Pname, strerror(errno));
- exit(EX_TEMPFAIL);
- }
- Fflush(stderr);
- Fflush(stdout);
- #if defined(SIGCHLD)
- Signal(SIGCHLD, childgone);
- #endif /* defined(SIGCHLD) */
- #if defined(SIGCLD)
- Signal(SIGCLD, childgone);
- #endif /* defined(SIGCLD) */
-
- if ((ChildPid = fork()) < 0) {
- Fprintf(stderr,"%s: Can't fork, %s.\n", Pname, strerror(errno));
- exit(EX_TEMPFAIL);
- }
- if (ChildPid == 0) {
- /* Redirect I/O; it's unlikely the test below will fail. */
- if (fd[PIPE_READER] != STDIN) {
- Close(STDIN);
- if (dup(fd[PIPE_READER]) != STDIN)
- Fprintf(stderr, "%s: Can't redirect input, %s.\n",
- Pname, strerror(errno));
- }
- Close(fd[PIPE_READER]);
- Close(fd[PIPE_WRITER]);
- (void)execv(iv[0], iv);
- Fprintf(stderr, "%s: Can't exec %s, %s.\n",
- Pname, iv[0], strerror(errno));
- exit(EX_OSERR);
- }
-
- /* Set things up after the fork. */
- Close(fd[PIPE_READER]);
- Signal(SIGPIPE, childgone);
- if ((F = fdopen(fd[PIPE_WRITER], "w")) == NULL)
- exit(EX_OSERR);
-
- /* Stuff the header. */
- if (!rfc822write(&H, F)) {
- Fprintf(stderr, "%s: Can't write header, %s.\n",
- Pname, strerror(errno));
- exit(EX_IOERR);
- }
-
- /* Write the rest of the message. */
- while (fgets(buff, sizeof buff, Infile)) {
- Fputs(buff, F);
- if (ferror(F))
- break;
- }
-
- /* Close down the pipe. */
- Fflush(F);
- if (ferror(F)) {
- Fprintf(stderr, "%s: Error flushing pipe to news, %s.\n",
- Pname, strerror(errno));
- exit(EX_IOERR);
- }
- if (fclose(F) == EOF)
- Fprintf(stderr, "%s: Error closing pipe to news, %s.\n",
- Pname, strerror(errno));
-
- /* Wait for inews, and exit as it does. */
- childgone();
- /* NOTREACHED */
- }
-